/*
 * Copyright (C) 2009 Kristopher T Babic
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see <http://www.gnu.org/licenses>.
 */

package edu.idp.client;

import gjt.BulletinLayout;
import gjt.Orientation;
import gjt.Util;
import edu.idp.client.anim.AdvancedSprite;
import edu.idp.client.anim.AnimationParameter;
import edu.idp.client.anim.AnimationPath;
import edu.idp.client.anim.CollisionArena;
import edu.idp.client.anim.CollisionDetector;
import edu.idp.client.anim.sequence.ModelSequence;
import edu.idp.client.anim.sequence.Sequence;
import edu.idp.client.draw.AdvancedDrawPanel;
import edu.idp.client.event.AnimationEvent;
import edu.idp.client.event.ServerStatusEvent;
import edu.idp.client.interfaces.AnimationEventListener;
import edu.idp.client.interfaces.ServerStatusListener;
import edu.idp.client.interfaces.SpriteAnimator;
import edu.idp.shared.StoredObject;
import edu.idp.shared.net.BusyFlag;
import edu.idp.shared.net.NetTransferObject;
import edu.idp.shared.net.NetTransport;
import edu.idp.shared.utils.Convert;

import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.net.Socket;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Vector;
//import gjt.Separator;

/**
 *
 * @author Kristopher T Babic
 */
public class InterDrawPanel extends AdvancedDrawPanel implements
                                                      AnimationEventListener, Runnable, CollisionArena, ActionListener, SpriteAnimator {

    private Thread sendThread = null;
    private Thread receiveThread = null;

    private boolean running = false;
    private boolean serverStatus = false;

    private Thread animationThread;
    private CollisionDetector edgeDetector;//, spriteDetector;
    private AdvancedSprite[] testSprite;
    private ModelSequence[] testSequence;

    private Vector serverStatusListeners = null;

    //  private   String              host      = new String("interdraw.ind.wpi.edu");
    private String host = new String("localhost");
    private int port = 1111;
    private String user_id = "default";
    private String img_nm = "default";
    private AnimationPanel animPanel = null;
    private boolean animDone = true;
    private boolean animFirst = false;
    private boolean inSynch = false;
    private long synchTime = 0;
    private boolean timeSet = false;

    private BusyFlag conFlag = new BusyFlag();
    private long lastUpdate = 0;
    private long lastSave = 0;
    private boolean handleErase = false;
    private Vector removedObjects = null;
    private ImageSelection imageSelection = null;

    private Socket connectionSocket = null;
    private UpdateThread updateThread = null;

    private String tmpObject = null;
    private char[] objectID;

    private Applet applet = null;


    public InterDrawPanel(Applet myApp) {
// 02-27-00    edgeDetector   = new EdgeCollisionDetector(this);
        setLayout(new BulletinLayout());

        this.applet = myApp;

        if (!synchronize()) {

// 4-9-2000      JOptionPane.showMessageDialog(this, "Cannot connect to server.  Please try again later.", "Error", JOptionPane.ERROR_MESSAGE);

            //Util.getFrame(this).dispose();
/*
      Thread t = new Thread() {
        public void run () {
          try { Thread.sleep(2000); } catch (Exception e) {}
          Frame f = Util.getFrame(InterDrawPanel.this);
          //System.out.println("before while");
          //while(!f.isShowing()){}
          InterDrawPanel.this.stop();
          f.dispose();
        }
      };
      t.start();
*/
        }
/*
    else {
      Thread iT = new Thread () {
        public void run () {
          InterDrawPanel.this.setEnabled(false);
          try { Thread.sleep(4000); } catch (Exception e) {}
          InterDrawPanel.this.init();
        }
      };
      iT.start();
    }
 */
        JOptionPane.showMessageDialog(this, "Remeber that you start up in a default image.\nPlease go to Image->Select to start you own image or select one already in progress.", "InterDraw -- Tip", JOptionPane.INFORMATION_MESSAGE);
        updateThread = new UpdateThread();
        updateThread.addActionListener(this);
        updateThread.start();
    }

    public void init() {
        retrieveAll();
        this.setEnabled(true);
    }

    public void getFlag() {
// System.out.println("get flag:" + conFlag.getBusyFlagOwner());
        conFlag.getBusyFlag();

    }

    public void releaseFlag() {
// System.out.println("free flag");
        conFlag.freeBusyFlag();
    }

    public void setSavedTime() {
        this.lastSave = Calendar.getInstance().getTime().getTime() + this.getTimeDifference();
    }

    public long getSavedTime() {
        return this.lastSave;
    }

    public void setUpdateTime() {
        this.lastUpdate = Calendar.getInstance().getTime().getTime() + this.getTimeDifference();
    }

    public long getUpdateTime() {
        return this.lastUpdate;
    }

    public long getTime() {
        return Calendar.getInstance().getTime().getTime() + this.getTimeDifference();
    }

    public void resetTimes() {
        resetSavedTime();
        resetUpdateTime();
    }

    public void resetSavedTime() {
        this.lastSave = 0L;
    }

    public void resetUpdateTime() {
        this.lastUpdate = 0L;
    }

    public void setHost(String h) {
        host = h;
    }

    public void setPort(int p) {
        port = p;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public void setServerStatus(boolean status) {
        this.serverStatus = status;
    }

    public boolean getServerStatus() {
        return this.serverStatus;
    }

    public Dimension getPreferredSize() {
        return new Dimension(500, 500);
    }

    public Dimension getMinimumSize() {
        return new Dimension(500, 500);
    }

    public void spriteCollision(AdvancedSprite sprite, AdvancedSprite other) {
    }

    public void edgeCollision(AdvancedSprite sprite, Orientation orientation) {
        Point loc = sprite.getLocation();
        java.awt.Rectangle bounds = this.getBounds();

        if (orientation == Orientation.LEFT)
            loc.x = 0;
        else if (orientation == Orientation.RIGHT)
            loc.x = bounds.width;
        else if (orientation == Orientation.TOP)
            loc.y = bounds.width;
        else if (orientation == Orientation.BOTTOM)
            loc.y = 0;
//System.out.println("edge collision");
        sprite.setLocation(loc.x, loc.y);
/*
		if (orientation == Orientation.RIGHT ||
			orientation == Orientation.LEFT)
			sprite.reverseX();
		else
			sprite.reverseY();
 */
    }

    public void stop() {
        running = false;
    }

    public boolean running() {
        return running;
    }

    public void start() {
        animationThread = new Thread(this);
        running = true;
        animationThread.start();
    }

    public void run() {

        while (running == true) {
            //edgeDetector.detectCollisions  ();
            //spriteDetector.detectCollisions();

            animateSprites();
            try {
                Thread.currentThread().sleep(AdvancedSprite.ANIM_TIME);
            }//100); }//50); }
            catch (Exception e) {
                e.printStackTrace();
            }
        }

        animationThread = null;
    }

    public Vector getSprites() {
        int ncomps = getComponentCount();
        Component comp;
        Vector vector = new Vector();

        for (int i = 0; i < ncomps; ++i) {
            if ((comp = getComponent(i)) instanceof AdvancedSprite)
                vector.addElement(comp);
        }
        return vector;
    }

    protected void animateSprites() {
//  System.out.println(this.getBounds());
        int ncomps = getComponentCount();
        Component comp;

        for (int i = 0; i < ncomps; ++i)
            if ((comp = getComponent(i)) instanceof AdvancedSprite)
                ((AdvancedSprite) comp).animate();
    }

    public Socket connect() {

        connectionSocket = null;
        fireEvent(new ServerStatusEvent(this, ServerStatusEvent.CONNECTING));
        try {
            connectionSocket = new Socket(getHost(), getPort());
            setServerStatus(false);
        }
        catch (Exception e) {
            fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_LOST));
            return null;
        }
        setServerStatus(true);
        fireEvent(new ServerStatusEvent(this, ServerStatusEvent.CONNECTED));
        return connectionSocket;

    }

    public void disconnect() {

        try {
            connectionSocket.close();
        }
        catch (Exception e) {
        }

    }

    protected void setSynched(boolean b) {
        this.inSynch = b;
    }

    public boolean isSynched() {
        return this.inSynch;
    }

    public boolean synchronize() {
//System.out.println("synchronizing.....");
        Socket s = connect();
        if (s == null) {
            setSynched(false);
            return false;
        }

        NetTransferObject nto = new NetTransferObject();

        setSynched(true);
//System.out.println("getting user id");

        if (getUserID().equals("") || getUserID().equals("default")) {

            try {

                nto.reset();
                nto.setCommand(NetTransferObject.CMD_GET_UID);
                nto.sendData(s);

                nto.readData(s);
                Enumeration enmr = nto.getData();
                String uid = null;
                if (enmr != null && enmr.hasMoreElements())
                    uid = (String) enmr.nextElement();

                if (uid == null || uid.equals(""))
                    setSynched(false);
                else
                    setUserID(uid);
            }
            catch (Exception e) {
                //System.out.println("get uid");
                e.printStackTrace();
                setSynched(false);
            }
        }
        //System.out.println("user id = \""+getUserID()+"\"");
// 02-19-00    try {s.close();} catch(Exception e) {}
//    s = connect();
        if (s == null) {
            setSynched(false);
            return false;
        }

        if (!timeSet) {
            //System.out.println("starting to look at server for time");
            try {

                nto.reset();
                nto.setCommand(NetTransferObject.CMD_GET_TIME);
                nto.addData(Convert.fromLong(Calendar.getInstance().getTime().getTime()));

                nto.sendData(s);
                nto.readData(s);

                String time = null;
                Enumeration enmr = nto.getData();
                if (enmr != null && enmr.hasMoreElements())
                    time = (String) enmr.nextElement();

                if (time == null || time.equals(""))
                    setSynched(false);
                else {
                    setTimeDifference(Convert.toLong(time));
                    timeSet = true;
                }
            }
            catch (Exception e) {
                //System.out.println("time catch");
                e.printStackTrace();
                setSynched(false);
            }
        }
//System.out.println("time difference = "+getTimeDifference());
//String ss = "time difference = "+getTimeDifference();
//javax.swing.JOptionPane.showMessageDialog(this, ss);
        try {
            s.close();
        }
        catch (Exception e) {
        }

        if (!isSynched()) {
            fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_LOST));
            return false;
        }

        return true;

    }

    protected void setTimeDifference(long time) {
        this.synchTime = time;
    }

    public long getTimeDifference() {
        return this.synchTime;
    }

    public void clearTools() {
        handleErase = false;
    }

    public void actionPerformed(ActionEvent event) {
        String command = event.getActionCommand();
        Object o = event.getSource();

        if (o != null && o == imageSelection) {
            imageSelection_ActionPerformed(command);
//      if (command != null)
//        setImageName(command);
//      setEnabled(true);
        }
        else if (o instanceof ToolButton) {
            if (command.equalsIgnoreCase("tool_ellipse")) {
                clearTools();
                drawEllipses();
            }
            else if (command.equalsIgnoreCase("tool_line")) {
                clearTools();
                drawLines();
            }
            else if (command.equalsIgnoreCase("tool_rectangle")) {
                clearTools();
                drawRectangles();
            }
            else if (command.equalsIgnoreCase("tool_pencil")) {
                clearTools();
                drawPencil();
            }
            else if (command.equalsIgnoreCase("tool_airbrush")) {
                clearTools();
                drawAirbrush();
            }
            else if (command.equalsIgnoreCase("tool_eraser")) {
                clearTools();
                eraseObjects();
            }
        }
        else if (command.equalsIgnoreCase("insert")) {
            insert_ActionPerformed(event);
        }
// 4-9-2000		else if (command.equalsIgnoreCase("send")) {
// 4-9-2000			//System.out.println("send");
// 4-9-2000			send_ActionPerformed(event, false);
// 4-9-2000		}
        else if (command.equalsIgnoreCase("update")) {
            //System.out.println("update");
//      System.out.println("updating...");
            send_ActionPerformed(event, false);
            update_ActionPerformed(event, false);
        }
        else if (command.equalsIgnoreCase("select")) {
            //System.out.println("select");
            select_ActionPerformed(event);
        }
        else if (command.equalsIgnoreCase("check")) {
            check_ActionPerformed(event);
        }
/*
    else if (command.equalsIgnoreCase("open")) {
      open_ActionPerformed(event);
    }
    else if (command.equalsIgnoreCase("new")) {
      new_ActionPerformed(event);
    }
 */
    }

    /*
     public void locateServer () {
         try {
         System.out.println("locating server from "+getHost()+":"+getPort());System.out.flush();
             Socket s = new Socket (getHost(), getPort());
             BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));

             String input = in.readLine();

             StringTokenizer st = new StringTokenizer(input, "|");
             setServerHost(st.nextToken());
             setServerPort(Integer.parseInt(st.nextToken()));

             System.out.println(getServerHost()+":"+getServerPort()); System.out.flush();
             s.close();
         }
         catch (Exception e) {
             System.err.println("locateServer error: " + e);
             System.err.flush();
         }
     }
      */

    protected void imageSelection_ActionPerformed(String image) {
        if (image != null && !image.equals(getImageName())) {
            removeAll();
            if (image.length() > 20)
                image = image.substring(0, 20);
            setImageName(image);
            resetTimes();
            retrieveAll();
        }
        this.setEnabled(true);
    }

    public void removeAll() {
        Component[] comps = this.getComponents();
        for (int i = 0; i < comps.length; i++) {
            if (comps[i] instanceof AdvancedSprite) {
                this.remove(comps[i]);
                ((AdvancedSprite) comps[i]).kill();
            }
        }
        super.removeAll();
    }

    public void setImageName(String nm) {
        if (nm == null) {
            img_nm = "";
            return;
        }
        if (nm.length() > NetTransport.MAX_LEN)
            img_nm = nm.substring(0, NetTransport.MAX_LEN);
        else
            img_nm = nm;
    }

    public String getImageName() {
        return img_nm;
    }

    public String getUserID() {
        return user_id;
    }

    public void setUserID(String uid) {
        this.user_id = uid;
    }

    /**
     * **************************************************
     * IMPLEMENTATION OF AnimationEventListener
     * ***************************************************
     */
    public void handleAnimationEvent(AnimationEvent ae) {
        int id = ae.getType();
        Util.getFrame(this).setEnabled(true);
//System.out.println("Received an AnimationEvent: " + id);
        if (id == AnimationPanel.EVENT_CANCEL) {
            Util.getFrame(this).setEnabled(true);
            super.restoreState();
        }
        else if (id == AnimationPanel.EVENT_LOCATION) {
            Object o = ae.getSource();
            if (o instanceof AnimationPanel)
                animPanel = (AnimationPanel) o;
            gjt.Util.getFrame(this).toFront();
            this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        }
        else if (id == AnimationPanel.EVENT_CREATE) {
            /** A BLESSED WORK OF ART IF THE THING ACTUALLY WORKS ....... **/

            AnimationParameter ap = ae.getSequenceParameters();
            Vector params = ap.getParameters();

            Class[] paramClasses = ap.getParameterClasses();

            Sequence animSeq = null;

            try {
                Class seqClass = Class.forName(ae.getSequenceName());
                java.lang.reflect.Constructor[] cs = seqClass.getConstructors();
//        Class classes[] = cs[1].getParameterTypes();
                java.lang.reflect.Constructor seqConst = seqClass.getConstructor(paramClasses);

                animSeq = (Sequence) seqConst.newInstance(params.toArray());
            }
            catch (Exception e) {
                e.printStackTrace();//System.out);
                return;
            }
//      System.out.println(ae.getAnimationPath());
            AnimationPath aPath = ae.getAnimationPath();
            aPath.setBounds(this.getBounds());
            aPath.setTimeDifference(this.getTimeDifference());
            aPath.reset();
            AdvancedSprite as = new AdvancedSprite(this, animSeq, aPath, getTimeDifference());//ae.getAnimationPath());

//      as.setCreationTime(this.getTime());

            as.setTimeDifference(this.getTimeDifference());
            as.setLifeSpan(ae.getLifeSpan());

            if (as != null) {
                this.add(as);
                saveObject(as.store());
            }
            if (as != null) {
                //System.out.println("added animation to panel");
                if (!running)
                    this.start();
            }
            //else
            //  System.out.println("didn't add animation to panel");
            super.restoreState();
        }

    }

/*
 * processMouseEvent()
 *
 *
 *
 */

    public void processMouseEvent(MouseEvent event) {

        int id = event.getID();
        if (animPanel == null) {
            if (id == MouseEvent.MOUSE_PRESSED && handleErase)
                processEraseEvent(event.getPoint());
            else
                super.processMouseEvent(event);
        }
        else {
            if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_CLICKED) {
                animPanel.setLocation(event.getPoint());
                animPanel = null;
                animDone = false;
                this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                Util.getFrame(this).setEnabled(false);
            }
        }
    }

    /**
     * *****************************************************
     * IMPLEMENTATION OF SpriteAnimator
     * ******************************************************
     */
    public void redrawToForeground(java.awt.Rectangle bounds) {
        super.blitBackgroundToWorkplace(bounds);
    }


    /**
     * ******************************************************
     * EVENT FIRES
     * *******************************************************
     */
    public synchronized void addServerStatusListener(ServerStatusListener se) {
        if (serverStatusListeners == null)
            serverStatusListeners = new Vector();
        serverStatusListeners.addElement(se);
    }

    public synchronized void removeServerStatusListener(ServerStatusListener se) {
        if (serverStatusListeners != null)
            serverStatusListeners.removeElement(se);
    }

    protected void fireEvent(ServerStatusEvent se) {
        if (serverStatusListeners == null)
            return;
        Vector targets;
        synchronized (this) {
            targets = (Vector) serverStatusListeners.clone();
        }

        for (int i = 0; i < targets.size(); i++) {
            ServerStatusListener target = (ServerStatusListener) targets.elementAt(i);
            target.handleServerStatusEvent(se);
        }

    }


    /**
     * ************************************************
     * MENU COMMAND ACTIONS
     * *************************************************
     */

    protected void insert_ActionPerformed(ActionEvent event) {
        Frame animWin = new ClosableFrame("Create an Animated Object", false);

        String osName = System.getProperty("os.name");
        boolean solarisWorkaround = (osName != null && (osName.indexOf("Solaris") != -1 || osName.indexOf("Irix") != -1));

        int diff = 0;
        if (solarisWorkaround)
            diff = 20;

        animWin.setSize(500, 310 + diff);
        animWin.add(new AnimationPanel(this, animWin, applet));
        animWin.setResizable(false);
        animWin.setVisible(true);
        super.saveState();
        super.drawNone();

        Util.getFrame(this).setEnabled(false);
    }

    protected synchronized void send_ActionPerformed(ActionEvent event, boolean waitFlg) {

// 4-9-2000    if (sendThread != null)
// 4-9-2000      while (sendThread.isAlive()) {}

        sendThread = new Thread() {

            public void run() {

                try {
                    getFlag();
                    Socket s = null;

                    NetTransferObject objects = null;

                    objects = InterDrawPanel.this.getSavedObjects(InterDrawPanel.this.getSavedTime());
                    if (objects == null && (InterDrawPanel.this.removedObjects == null || InterDrawPanel.this.removedObjects.size() <= 0)) {// || objects.length() == 0){
                        System.out.println("nothing to send.");
                        releaseFlag();
                        return;
                    }

                    s = InterDrawPanel.this.connect();//new Socket(getHost(), getPort());
                    s.setSoTimeout(10000);
//02-21-00          if (s == null)

                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SENDING_DATA));

                    if (objects == null)
                        objects = new NetTransferObject();

                    objects.setCommand(NetTransferObject.CMD_ADD);
                    objects.setImageName(InterDrawPanel.this.getImageName());
                    objects.setUID(InterDrawPanel.this.getUserID());

                    if (InterDrawPanel.this.removedObjects != null) {
//System.out.println("removed Objects size = "+InterDrawPanel.this.removedObjects.size());
                        for (int i = 0; i < InterDrawPanel.this.removedObjects.size(); i++) {
                            //StoredObject so = (StoredObject)InterDrawPanel.this.removedObjects.elementAt(i);
                            String so = (String) InterDrawPanel.this.removedObjects.elementAt(i);
//for (int ii = 0; ii < so.length(); ii++)
//  System.out.print((int)so.charAt(ii)+" ");
//System.out.println();
                            objects.addRemove(so);//.getID());
                        }
                    }

                    objects.sendData(s);
                    objects.readData(s);

                    if (objects.getCommand() == NetTransferObject.OK) {
                        InterDrawPanel.this.setSavedTime();
                        if (InterDrawPanel.this.removedObjects != null)
                            InterDrawPanel.this.removedObjects.clear();

                        System.out.println("send completed sucessfully");
//javax.swing.JOptionPane.showInternalMessageDialog(InterDrawPanel.this, "invalid object received", "error", javax.swing.JOptionPane.ERROR_MESSAGE);

                        InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.CONNECTED));

                    }
                    else {
                        System.out.println("send failed");
// 4-9-2000            javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "error on server side on send", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                        InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_ERROR));
                    }
// 4-9-2000         s.close();

                }
                catch (Exception e) {
                    e.printStackTrace();
                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_LOST));
                }
                disconnect();
                releaseFlag();
            }
        };
        sendThread.start();

        if (waitFlg) {
            while (sendThread.isAlive()) {
            }
        }
    }

    protected synchronized void update_ActionPerformed(ActionEvent event, boolean allFlg) {

// 4-9-2000    if (receiveThread != null)
// 4-9-2000      while (receiveThread.isAlive()) {}


        receiveThread = new Thread() {
            public void run() {

                try {
                    getFlag();
                    Socket s = null;
//System.out.println("connecting to server");
                    s = InterDrawPanel.this.connect();//new Socket(getHost(),getPort());
                    s.setSoTimeout(30000);

//			    String uid = "bad babic";
//			    String iname = "babics pic";

                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.RECEIVING_DATA));

//System.out.println("creating net transfer object");
                    NetTransferObject nto = new NetTransferObject();

//          if (allFlg) {
                    nto.setCommand(NetTransferObject.CMD_GET_NEW);
                    nto.addData(Convert.fromLong(InterDrawPanel.this.getUpdateTime()));
//          }
//          else
//            nto.setCommand(NetTransferObject.CMD_GET_NEW);

                    nto.setImageName(InterDrawPanel.this.getImageName());
                    nto.setUID(InterDrawPanel.this.getUserID());

//          if (InterDrawPanel.this.removedObjects != null) {
//            for (int i = 0; i < InterDrawPanel.this.removedObjects.size(); i++)
//              objects.addRemove((String)InterDrawPanel.this.removedObjects.elementAt(i));
//          }
//System.out.println("sending commands");
                    nto.sendData(s);
/***********NEED TO MOVE THIS SOMEWHERE OR ADD MORE ERROR CHECKING ON BOTH SIDES
 *******************************************************************************/
//System.out.println("reading data");
                    nto.readData(s);

                    if (nto.getCommand() == NetTransferObject.ERROR) {
                        System.out.println("update data failure");
// 4-9-2000            javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "update failed on server side", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                        InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_ERROR));
                        releaseFlag();
                        return;
                    }

                    InterDrawPanel.this.setUpdateTime();
//          InterDrawPanel.this.removedObjects.clear();

                    Enumeration enmr = nto.getData();

//          if (enmr = null)
//            return;

                    boolean returnFlg = false;
//System.out.println("restoring objects");
                    while (enmr != null && enmr.hasMoreElements()) {
                        Object tmp = enmr.nextElement();

                        if (!StoredObject.isValid(tmp)) {
                            System.out.println("received an invalid object");
                            javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "invalid object received", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                            continue;
                        }

//String tt = tmp.toString();
//for (int j = 0; j < tt.length(); j++)
//  System.out.print((int)tt.charAt(j)+" ");
//System.out.println();

                        InterDrawPanel.this.saveExternalObject(new StoredObject(tmp));
                        returnFlg = true;
                    }
//System.out.println("removing objects");
                    enmr = nto.getRemovedData();
                    while (enmr != null && enmr.hasMoreElements()) {
                        String tmp = (String) enmr.nextElement();

                        if (tmp == null || tmp.equals("")) {//!StoredObject.isValid(tmp)) {
                            System.out.println("received an invalid object");
                            javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "invalid object received", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                            continue;
                        }

                        InterDrawPanel.this.removeExternalObjectByID(tmp);
//            InterDrawPanel.this.removeObject(new StoredObject(tmp));
                        returnFlg = true;
                    }

                    if (returnFlg) {
                        restoreAll();
                        //blitWorkplaceToScreen();
                    }

                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.CONNECTED));

// 4-9-2000		      s.close();
                }
                catch (Exception e) {
                    e.printStackTrace();
                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_ERROR));
                }
                InterDrawPanel.this.disconnect();
                releaseFlag();
            }
        };
        receiveThread.start();

//    if (waitFlg) {
//      while (receiveThread.isAlive()) { try {Thread.sleep(100);}catch(Exception e){} }
//    }
    }

    protected synchronized void retrieveAll() {

// 4-9-2000    if (receiveThread != null)
//4-9-2000      while (receiveThread.isAlive()) {}


        receiveThread = new Thread() {
            public void run() {

                try {
                    getFlag();
                    Socket s = null;

                    s = InterDrawPanel.this.connect();
                    s.setSoTimeout(10000);

                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.RECEIVING_DATA));

                    NetTransferObject nto = new NetTransferObject();

                    nto.setCommand(NetTransferObject.CMD_GET_ALL);

                    nto.setImageName(InterDrawPanel.this.getImageName());
                    nto.setUID(InterDrawPanel.this.getUserID());

                    nto.sendData(s);

                    nto.readData(s);

                    if (nto.getCommand() == NetTransferObject.ERROR) {
                        System.out.println("update data failure");
                        javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "update failed on server side", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                        InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.SERVER_ERROR));
                        releaseFlag();
                        return;
                    }

                    InterDrawPanel.this.setUpdateTime();

                    Enumeration enmr = nto.getData();

                    boolean returnFlg = false;

                    while (enmr != null && enmr.hasMoreElements()) {
                        Object tmp = enmr.nextElement();

                        if (!StoredObject.isValid(tmp)) {
                            System.out.println("received an invalid object");
                            javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "invalid object received", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                            continue;
                        }

                        InterDrawPanel.this.saveExternalObject(new StoredObject(tmp));
                        returnFlg = true;
                    }

                    enmr = nto.getRemovedData();
                    while (enmr != null && enmr.hasMoreElements()) {
                        String tmp = (String) enmr.nextElement();

                        if (tmp == null || tmp.equals("")) {
                            System.out.println("received an invalid object");
                            javax.swing.JOptionPane.showMessageDialog(InterDrawPanel.this, "invalid object received", "error", javax.swing.JOptionPane.ERROR_MESSAGE);
                            continue;
                        }

                        InterDrawPanel.this.removeExternalObjectByID(tmp);

                        returnFlg = true;
                    }

                    if (returnFlg)
                        restoreAll();

                    InterDrawPanel.this.fireEvent(new ServerStatusEvent(this, ServerStatusEvent.CONNECTED));

// 4-9-2000		      s.close();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
                InterDrawPanel.this.disconnect();
                releaseFlag();
            }

        };
        receiveThread.start();

    }

    protected void select_ActionPerformed(ActionEvent event) {
        try {
            getFlag();
            Socket s = connect();

            NetTransferObject nto = new NetTransferObject();
            nto.setCommand(NetTransferObject.CMD_GET_IMAGES);

            nto.sendData(s);
            nto.readData(s);
//System.out.println("after send receive");
            if (nto.getCommand() == NetTransferObject.ERROR) {
                JOptionPane.showMessageDialog(this, "Server side error, try again later.", "error", JOptionPane.ERROR_MESSAGE);
                releaseFlag();
                return;
            }

            Enumeration enmr = nto.getData();
            Vector data = new Vector();

            while (enmr != null && enmr.hasMoreElements())
                data.addElement(enmr.nextElement());

            String[] list = new String[data.size()];

            for (int i = 0; i < data.size(); i++)
                list[i] = (String) data.elementAt(i);

            imageSelection = new ImageSelection(list);
            imageSelection.addActionListener(this);
            imageSelection.setVisible(true);

            this.setEnabled(false);

        }
        catch (Exception e) {
            //JOptionPane.showMessageDialog(this, "Server connection lost, try again later.", "error", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
        disconnect();
        releaseFlag();

    }

/*
  public synchronized void open_ActionPerformed(ActionEvent event){
    System.out.println("open action");
  }

  public synchronized void new_ActionPerformed(ActionEvent event){
    System.out.println("new action");
  }
 */

    protected void check_ActionPerformed(ActionEvent event) {
        Socket s = connect();
        try {
            s.close();
        }
        catch (Exception e) {
        }
    }

    ///////////////////////// make sure class isn't abstract! //////////////////////
    protected void restoreOther(StoredObject object) { //String object) {
        int type = object.getType(); //DrawType.getType(object);

        if (type == StoredObject.ANIMATED) { //DrawType.ANIMATED){
//System.out.println("Restoring an animated object.xx");
            try {
                AdvancedSprite as = new AdvancedSprite(this);
//System.out.println("Restoring an animated object.xx1");
                as.restore(object);
                as.setTimeDifference(this.getTimeDifference());

//System.out.println("Restoring an animated object.xx2");

                AnimationPath ap = as.getPath();
                ap.setBounds(this.getBounds());
                as.setPath(ap);

/*
////// it is calling setlocation somewhere before this.  I'm not sure exactly where.
      Point loc = as.getLocation();
System.out.println("location of sprite when restored: ("+loc.x+","+loc.y+")");
      java.awt.Rectangle bounds = getBounds();
      loc.x = loc.x % bounds.width;
      loc.y = loc.y % bounds.height;
////System.out.print(loc+"->");

      if (loc.x < 0)
        loc.x = bounds.width - loc.x;
      if (loc.y < 0)
        loc.y = bounds.height - loc.y;
System.out.println("location of sprite after being set: ("+loc.x+","+loc.y+")");

      as.setLocation(loc);  //.x, loc.y);
////System.out.println(loc);
*/

                this.add(as);
                if (!this.running())
                    this.start();

                removeExternalObject(object);
            }
            catch (Exception ex) {
            }
////System.out.flush();
        }
    }

    public void eraseObjects() {
        super.drawNone();
        handleErase = true;
    }

    private void processEraseEvent(Point loc) {

/*
    Component comp = this.getComponentAt(loc);
    StoredObject so == null;

    if (comp != null && comp instanceof AdvancedSprite) {
      so = ((AdvancedSprite
 */
        StoredObject so = findObjectAt(loc);
        if (so == null)
            return;

        if (removedObjects == null)
            removedObjects = new Vector();

        removedObjects.addElement(so.getID());

        removeObject(so);
        java.awt.Rectangle bounds = so.getBounds();

        //clear(new java.awt.Rectangle(bounds.x-5, bounds.y-5, bounds.width+10, bounds.height+10));
        restoreAll(new java.awt.Rectangle(bounds.x - 5, bounds.y - 5, bounds.width + 10, bounds.height + 10));
        //blitWorkplaceToScreen(so.getBounds());

    }

    protected String createObjectID() {
        if (objectID == null) {
            objectID = new char[2];
            for (int i = 0; i < 2; i++)
                objectID[i] = (char) 1;
        }

//			String newId = new String(id);

        objectID[1]++;
        if (objectID[1] == Character.MAX_VALUE) {
            objectID[0]++;
            objectID[1] = 1;
        }

        String retString = new String(objectID);
        retString += getUserID();
        return retString;
    }
}
